package com.damai.action.meta;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.annotate.JsonIgnore;

import com.citywithincity.api.ApiBuildUtil;
import com.citywithincity.api.ApiConfig;
import com.citywithincity.api.entities.Customer;
import com.citywithincity.api.utils.ApiUtil;
import com.citywithincity.db.Entity;
import com.citywithincity.db.EntityBuilder;
import com.citywithincity.db.EntityField;
import com.citywithincity.db.Page;
import com.citywithincity.db.meta.ColumnStruct;
import com.citywithincity.reflect.CtClassUtil;
import com.citywithincity.utils.ClassUtil;
import com.citywithincity.utils.StringUtils;
import com.damai.action.IActionFactory;
import com.damai.annotation.ApiMeta;
import com.damai.annotation.Crypt;
import com.damai.annotation.Param;
import com.damai.annotation.Token;
import com.jzoom.zoom.aop.reflect.ReflectKit;

import javassist.ClassPool;

public class ApiMetaDataFactory {
	
	/**
	 * 
	 * @param method
	 * @return
	 */
	private static Class<?> getRealReturnType(Method method){
		Type type = method.getGenericReturnType();
		if (type instanceof ParameterizedType) { // 判断获取的类型是否是参数类型
           
            Type[] typesto = ((ParameterizedType) type).getActualTypeArguments();// 强制转型为带参数的泛型类型，
            if(typesto[0] instanceof Class){
            	return (Class<?>)typesto[0];
            }
            
        }
		return null;
	}
	
	private static String calcReturnType(ApiMetaData data ,Class<?> returnType){
		
		if(ClassUtil.isSimple(returnType)){
			return returnType.getSimpleName();
		}
		
		if(
				returnType == List.class || 
				returnType == Set.class || 
				returnType == Collection.class)
		{
			//结果泛型
			return "Array";
		}else if(returnType == Page.class){
			return "Page";
		}else if(returnType == Void.class){
			return "void";
		}else if(returnType == HttpServletRequest.class){
			return "Request";
		}else if(returnType == Map.class){
			return "Map";
		}
	
		try{
			data.setResults(getBeanParams(returnType));  
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return "Object";
	}
	
	 
	private static List<ParamInfo> getBeanParams(Class<?> bean){
		if(bean==null)return null;
		//这里可以使用
		if(bean==Object.class)return null;
		List<ParamInfo> list = new ArrayList<ParamInfo>();
		
		
		Entity<?> entity = EntityBuilder.getEntity(bean);
		if(entity!=null){
			for (EntityField field : entity.getFields()) {
				
				if(field.getField().isAnnotationPresent(JsonIgnore.class)){
					continue;
				}
				if(field.getField().isAnnotationPresent(ParamIgnore.class)){
					continue;
				}
				ColumnStruct struct = field.getColumnStruct();
				ParamInfo info = new ParamInfo();
				info.setName(field.getField().getName());
				info.setType(field.getField().getType().getSimpleName());
				info.setComment(struct.getComment());
				
				if(field.getField().isAnnotationPresent(Param.class)){
					setParamInfos(field.getField().getType(),field.getField().getAnnotation(Param.class),info);
				}else{
					
				}
				list.add(info);
			}
			
		}else{ 
			if(bean==Object.class){
				return Collections.emptyList();
			}
			//说明没有注册过
			Field[] fields = ClassUtil.getFields(bean);
			for (Field field : fields) {
				ParamInfo info = new ParamInfo();
				info.setName(field.getName());
				info.setType(field.getType().getSimpleName());
				if(field.isAnnotationPresent(Param.class)){
					setParamInfos(field.getType(),field.getAnnotation(Param.class),info);
				}else{
					
				}
				list.add(info);
			}	
		}
		return list;
	}
	
	/**
	 * 根据类型获取
	 * @param type
	 * @return
	 */
	private static String getDefaultHelperType(Class<?> type  ){
		if(type == byte[].class){
			return ParamHelperTypes.image;
		}
		
		return ParamHelperTypes.input;
	}
	
	private static void setParamInfos( Class<?> type, Param param,ParamInfo paramInfo){
		if(!StringUtils.isEmpty(param.comment())){
			paramInfo.setComment(param.comment());
		}else{
			if(paramInfo.getComment() == null){
				paramInfo.setComment("");
			}
		}
		if(StringUtils.isEmpty(param.helperType())){
			paramInfo.setHelperType(getDefaultHelperType(type));
		}else{
			paramInfo.setHelperType(param.helperType());
		}
		paramInfo.setMock(  param.mock()  );
		paramInfo.setExtra(toExtra(param.extra()));
	}
	
	
	private static Map<String,String> toExtra(String src){
		if(StringUtils.isEmpty(src))return null;
		String[] args = src.split("\\|");
		
		Map<String, String> result = new LinkedHashMap<String, String>();
		for (String arg : args) {
			String[] kv = arg.split(":");
			result.put(kv[0], kv[1]);
		}
		return result;
	}
	
	public static ApiMetaData create(String key,Class<?> controllerClass, Method method,ClassPool classPool ){
		
		Class<? extends IActionFactory> factoryClass = ApiBuildUtil.getFactoryClass(controllerClass, method);
		int apiType = ApiUtil.getApiType(factoryClass);
		ApiMetaData data = new ApiMetaData(apiType);
		data.setName(key.substring(1));
		Class<?> returnType = method.getReturnType();
		
		
		
		
		
		//forList
		//解析returnType
		ApiMeta meta = method.getAnnotation(ApiMeta.class);
		if(meta!=null){
			data.setComment(meta.comment());
			Class<?> pClass = meta.returnParameterized();
			if(pClass!=ApiMeta.class){
				data.setResults(getBeanParams(pClass));
			}
		}
		data.setReturnType(calcReturnType(data,returnType));
		
		if(data.getResults() == null){
			/**
			 * 
			 */
			if(returnType == List.class || returnType == Set.class || returnType == Page.class || returnType == Collection.class){
				data.setResults(getBeanParams(getRealReturnType(method)));
			}
		}
		
		try{
			String[] names = CtClassUtil.getParams(classPool, controllerClass, method);
			int index = 0;
			//boolean needsLogin = false;
			Annotation[][] an = method.getParameterAnnotations();
			List<ParamInfo> list = new ArrayList<ParamInfo>(names.length);
			for (Class<?> type : method.getParameterTypes()) {
				do{
					Param param = null;
					if(an[index] != null ){
						for (Object p : an[index]) {
							if(p instanceof Param){
								param = (Param)p;
							}
						}
					}
					if(type==Customer.class 
							|| type == HttpServletRequest.class 
							|| type == HttpServletResponse.class 
							|| type==Map.class){
						break;
					}
					
					if(Map.class.isAssignableFrom(type) ){
						break;
					}
					if(type.isPrimitive() 
							|| type.getName().startsWith("java.lang.") 
							|| type == byte[].class ){
						ParamInfo paramInfo = new ParamInfo();
						if(param!=null){
							setParamInfos(type,param, paramInfo);
						}
						paramInfo.setName(names[index]);
						paramInfo.setType(type.getSimpleName());
						
						list.add(paramInfo);
					}else if(names[index].equals("json")){
						//json
						list.addAll(getBeanParams(type));
						
					}else if(type==List.class){
						list.addAll(getBeanParams(  ReflectKit.getGenericParameterType(method, index,0) ));
						
					}else{
						//param is bean
						list.addAll(getBeanParams(type));
					}
				}while(false);
				++index;
			}
		
			Crypt crypt = method.getAnnotation(Crypt.class);
			if(crypt==null){
				data.setCrypt(Crypt.NONE);
			}else{
				data.setCrypt(crypt.value());
			}
			
			Token token = method.getAnnotation(Token.class);
			if(token==null){
				data.setToken(0);
			}else if(token.value()){
				data.setToken(2);
				data.setNeedsLogin(true);
			}else{
				data.setToken(1);
			}
			
			data.setParams(list);
		}catch(Exception e){
			
			ApiConfig.error("Create param getter error:" + controllerClass.getName() + " method:" + method.getName(),e);
			throw new RuntimeException(e);
		}
		return data;
	}
	
	
	
	public String getSimpleType(Class<?> type) {
		return type.getSimpleName();
	}
	
	
	private static Map<String, Class<?>> typeMap = new HashMap<String, Class<?>>();
	static {
		typeMap.put("int", int.class);
		typeMap.put("double", double.class);
		typeMap.put("float", float.class);
		typeMap.put("long", long.class);
		typeMap.put("byte", byte.class);
		typeMap.put("short", short.class);
		
		typeMap.put("Integer", Integer.class);
		typeMap.put("String", String.class);
		
		typeMap.put("Map", Map.class);
	}
	
	public static Class<?> simpleTypeToClass(String type) {
		return typeMap.get(type);
	}
	
	
}
